package org.ebookdroid.core.decrypter;

import java.io.InputStream;
import java.io.OutputStream;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

/**
 * 
 * @author shaoyuru@whu.edu.cn
 *
 */
public class Decrypter {
	Cipher dcipher;
	
	/**
	 * Construct a decrypter object 
	 * @param key Use this key to generate secret key
	 * @param saltStr Special parameter to generate secret key
	 */
	public Decrypter(String key, String saltStr) {
		byte[] salt = makeSalt(saltStr);
		PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 20);
		PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray());
		
		try {
			SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
			SecretKey skey = skf.generateSecret(keySpec);
			dcipher = Cipher.getInstance("PBEWithMD5AndDES");
			dcipher.init(Cipher.DECRYPT_MODE, skey, paramSpec);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * Make a 8 bytes salt from a string
	 * @param saltStr The string to make salt
	 * @return A 8 bytes salt
	 */
	private byte[] makeSalt(String saltStr) {
		byte[] saltBytes = saltStr.getBytes();
		int len = saltBytes.length;
		byte[] s = new byte[8];
		
		if (len < 8) {
			for (int i = 0; i < len; i++) 
				s[i] = saltBytes[i];
			for (int i = len; i < 8; i++)
				s[i] = (byte)(i * 10);
		} else { // len >= 8, pick out first 8 bytes
			for (int i = 0; i < 8; i++)
				s[i] = saltBytes[i];
		}
		
		return s;
	}
	
	public Cipher getCipher() {
		return dcipher;
	}
	
	/**
	 * Decrypt the input stream and output 
	 * @param in InputStream that will be decrypted
	 * @param out OutputStream that have been decrypted
	 */
	public void decrypt(InputStream in, OutputStream out) {
		byte[] buf = new byte[1024];
		try {
			in = new CipherInputStream(in, dcipher);
			int read = 0;
			while ((read = in.read(buf)) > 0) {
				out.write(buf, 0, read);
			}
			in.close();
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public byte[] decrypt(byte[] input) {
		byte[] encrypted = null;
		try {
			encrypted = dcipher.doFinal(input);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return encrypted;
	}
}
